#ifndef X_CONFIGMANAGER_H
#define X_CONFIGMANAGER_H

#include <wx/hashmap.h>
#include <wx/hashset.h>
#include "globals.h"
#include <tinyxml.h>
#include "manager.h"
#include "base64.h"
#undef new
#include <map>
#include <set>

class ISerializable {
  public:
    ISerializable();
    virtual ~ISerializable();
    virtual wxString SerializeOut() const = 0;
    virtual void SerializeIn( const wxString& s ) = 0;
};

namespace ConfigManagerContainer {
  typedef std::map<wxString, wxString> StringToStringMap;
  typedef std::map<int, wxString> IntToStringMap;
  typedef std::set<wxString> StringSet;
  typedef std::map<wxString, ISerializable*> SerializableObjectMap;
}

enum SearchDirs {
  sdHome = 0x0001,  // /home/m
  sdBase = 0x0002,  // /usr/bin
  sdTemp = 0x0004,  // /tmp
  sdPath = 0x0008,
  sdConfig = 0x0010,  // /home/m/.config/m
  sdCurrent = 0x0020,  // /home/m/m
  sdPluginsUser = 0x0100,  // /home/m/.local/share/m/plugins
  sdScriptsUser = 0x0200,  // /home/m/.local/share/m/scripts
  sdDataUser = 0x0400,  // /home/m/.local/share/m
  sdAllUser = 0x0f00,
  sdPluginsGlobal = 0x1000,  // /usr/share/m/plugins
  sdScriptsGlobal = 0x2000,  // /usr/share/m/scripts
  sdDataGlobal = 0x4000,  // /usr/share/m
  sdAllGlobal = 0xf000,
  sdAllKnown = 0xffff
};


class M_Code_App;

class ConfigManager {
    friend class CfgMgrBldr;
    friend class M_Code_App;
    TiXmlDocument *doc;
    TiXmlElement* root;
    TiXmlElement* pathNode;
    ConfigManager( TiXmlElement* r );
    TiXmlElement* AssertPath( wxString& path );
    TiXmlElement* GetUniqElement( TiXmlElement* p, const wxString& q );
    void SetNodeText( TiXmlElement *n, const TiXmlText& t );
    inline void Collapse( wxString& str ) const;
    wxString InvalidNameMessage( const wxString& what, const wxString& sub, TiXmlElement *localPath ) const;
    static void InitPaths();
    static void MigrateFolders();
    static wxString config_folder;
    static wxString home_folder;
    static wxString data_path_user;
    static wxString data_path_global;
    static wxString app_path;
    static wxString temp_folder;
    static wxString alternate_user_data_path;
    static bool has_alternate_user_data_path;
  protected:
    static bool SetUserDataFolder( const wxString &user_data_path );
    static wxString GetUserDataFolder();
  public:
    static wxString LocateDataFile( const wxString& filename, int search_dirs = sdAllKnown );
    static wxString GetFolder( SearchDirs dir );
    static wxString GetHomeFolder() { return GetFolder( sdHome ); }
    static wxString GetConfigFolder() { return GetFolder( sdConfig ); }
    static wxString GetPluginsFolder( bool global = true ) { return GetFolder( global ? sdPluginsGlobal : sdPluginsUser ); }
    static wxString GetScriptsFolder( bool global = true ) { return GetFolder( global ? sdScriptsGlobal : sdScriptsUser ); }
    static wxString GetDataFolder( bool global = true ) { return GetFolder( global ? sdDataGlobal : sdDataUser ); }
    static wxString GetExecutableFolder() { return GetFolder( sdBase ); }
    static wxString GetTempFolder() { return GetFolder( sdTemp ); }
    static wxString GetProxy();
    static inline wxString ReadDataPath() {return GetDataFolder();}
    static inline wxString ReadAppPath() {return GetExecutableFolder();}
    wxString GetPath() const;
    void SetPath( const wxString& strPath );
    wxArrayString EnumerateSubPaths( const wxString& path );
    wxArrayString EnumerateKeys( const wxString& path );
    void DeleteSubPath( const wxString& strPath );
    void Clear();
    void Delete();
    void DeleteAll();
    void Flush();
    void Write( const wxString& name,  const wxString& value, bool ignoreEmpty = false );
    wxString Read( const wxString& key, const wxString& defaultVal = wxEmptyString );
    bool Read( const wxString& key, wxString* str );
    void Write( const wxString& key, const char* str );
    void Write( const wxString& name,  int value );
    bool Read( const wxString& name,  int* value );
    int  ReadInt( const wxString& name,  int defaultVal = 0 );
    void Write( const wxString& name,  bool value );
    bool Read( const wxString& name,  bool* value );
    bool ReadBool( const wxString& name,  bool defaultVal = false );
    void Write( const wxString& name,  double value );
    bool Read( const wxString& name,  double* value );
    double ReadDouble( const wxString& name,  double defaultVal = 0.0f );
    bool Exists( const wxString& name );
    void Set( const wxString& name );
    void UnSet( const wxString& name );
    void Write( const wxString& name,  const wxArrayString& as );
    void Read( const wxString& name,  wxArrayString* as );
    wxArrayString ReadArrayString( const wxString& name );
    void WriteBinary( const wxString& name,  const wxString& source );
    void WriteBinary( const wxString& name,  void* ptr, size_t len );
    wxString ReadBinary( const wxString& name );
    void Write( const wxString& name,  const wxColour& c );
    bool Read( const wxString& name, wxColour* value );
    wxColour ReadColour( const wxString& name, const wxColour& defaultVal = *wxBLACK );
    void Write( const wxString& name, const ISerializable& object );
    bool Read( const wxString& name, ISerializable* object );
    void Write( const wxString& name, const ConfigManagerContainer::StringToStringMap& map );
    void Read( const wxString& name, ConfigManagerContainer::StringToStringMap* map );
    ConfigManagerContainer::StringToStringMap ReadSSMap( const wxString& name );
    void Write( const wxString& name, const ConfigManagerContainer::IntToStringMap& map );
    void Read( const wxString& name, ConfigManagerContainer::IntToStringMap* map );
    ConfigManagerContainer::IntToStringMap ReadISMap( const wxString& name );
    void Write( const wxString& name, const ConfigManagerContainer::StringSet& set );
    void Read( const wxString& name, ConfigManagerContainer::StringSet* map );
    ConfigManagerContainer::StringSet ReadSSet( const wxString& name );
    void Write( const wxString& name, const ConfigManagerContainer::SerializableObjectMap* map );
    template <typename T> void Read( const wxString& name, std::map<wxString, T*> *map ) {
      wxString key( name );
      TiXmlHandle ph( AssertPath( key ) );
      TiXmlElement* e = 0;
      if( TiXmlNode *n = ph.FirstChild( key.mb_str( wxConvUTF8 ) ).FirstChild( "objmap" ).Node() )
        while( n->IterateChildren( e ) && ( e = n->IterateChildren( e )->ToElement() ) ) {
          T *obj = new T;
          obj->SerializeIn( wxBase64::Decode( cbC2U( e->FirstChild()->ToText()->Value() ) ) );
          ( *map )[cbC2U( e->Value() )] = obj;
        }
    }
};


class ConfigManagerWrapper {
  public:
    ConfigManagerWrapper() {}
    ConfigManagerWrapper( wxString namespace_, wxString basepath ) : m_namespace( namespace_ ), m_basepath( basepath ) {
      if( !m_basepath.EndsWith( _T( "/" ) ) ) {
        m_basepath += _T( "/" );
      }
    }
    bool IsValid() const { return !m_namespace.empty(); }
    const wxString& GetBasepath() const { return m_basepath; }
    void Write( const wxString& name, const wxString& value, bool ignoreEmpty = false );
    wxString Read( const wxString& key, const wxString& defaultVal = wxEmptyString );
    bool Read( const wxString& key, wxString* str );
    void Write( const wxString& key, const char* str );
    void Write( const wxString& name,  int value );
    bool Read( const wxString& name,  int* value );
    int  ReadInt( const wxString& name,  int defaultVal = 0 );
    void Write( const wxString& name,  bool value );
    bool Read( const wxString& name,  bool* value );
    bool ReadBool( const wxString& name,  bool defaultVal = false );
    void Write( const wxString& name, double value );
    bool Read( const wxString& name, double* value );
    double ReadDouble( const wxString& name, double defaultVal = 0.0f );

  private:
    wxString m_namespace;
    wxString m_basepath;
};


wxArrayString cbReadBatchBuildPlugins();

void cbWriteBatchBuildPlugins( wxArrayString bbplugins, wxWindow *messageBoxParent );

WX_DECLARE_STRING_HASH_MAP( ConfigManager*, NamespaceMap );

class CfgMgrBldr : public Mgr<CfgMgrBldr> {
    friend class ConfigManager;
    friend class Mgr<CfgMgrBldr>;
    NamespaceMap namespaces;
    TiXmlDocument *doc;
    TiXmlDocument *volatile_doc;
    wxCriticalSection cs;
    bool r;
    wxString cfg;
    void Flush();
    void Close();
    void SwitchTo( const wxString& absFN );
    void SwitchToR( const wxString& absFN );
    ConfigManager* Build( const wxString& name_space );
    wxString FindConfigFile( const wxString& filename );
    wxString DetermineExecutablePath();
  protected:
    CfgMgrBldr();
    ~CfgMgrBldr();
  public:
    static ConfigManager* GetConfigManager( const wxString& name_space );
};

#endif
